package com.hengtiansoft.lfy.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 男女声识别 基音判断法 0-200HZ 男 150-400HZ 女 故大于170为女 小于170为男
 * 采用VAD过滤有效语音 短时自相关法算出每帧的基音 最后使用中值滤波去除干扰 推算平均HZ
 * @author suyiyang
 * 
 */

public class MFRecognize {
	/**
	 * 最大门限值值
	 */
	private static int MAX_MUTE_VALUE = 6000;

	/**
	 * 静音判断取语音头长度时间(毫秒)
	 */
	private static final int GET_HEAD_MUTE_FRAME = 10;

	/**
	 * 最小门限值
	 */
	private static int DEFAULT_MUTE_VALUE = 400;

	private int frameCount = 0;

	public int gateMuteValue = DEFAULT_MUTE_VALUE;

	public int headVoiceSum = 0;
	/**
	 * 最大正常HZ数
	 */
	public static int MAX_HZ = 500;

	public static int CUTTING_LINE_HZ = 170;

	/**
	 *  vad是否开启
 	 */
	private boolean isVadStart = false;
	/**
	 * 每一帧有效语音的基音判断值
	 */
	private ArrayList<Integer> T = new ArrayList<>();

	/**
	 * 最大样本数 40ms*150 = 6sec
	 */
	private int maxCount = 150;

	/**
	 * 获取说话人的HZ数
	 * @return
     */
	public int getFinallyHz() {

		ArrayList<Integer> T1 = getSampleByMedianFilter(T);

		if(T1.size() <= 0)
			return 0;
		long sum = 0;
		for (int i = 0; i < T1.size(); i++) {
			sum += T1.get(i);
		}
		int avg = (int) (sum / T1.size());

		reset();
		return avg;
	}

	// 状态复位
	public void reset() {

		headVoiceSum = 0;
		gateMuteValue = DEFAULT_MUTE_VALUE;
		frameCount = 0;
		isVadStart = false;
		T.clear();
	}

	/**
	 * 送入录音原始数据
	 * @param b
	 * @param offset
	 * @param count
     */
	public void inputWave(byte[] b, int offset, int count) {
		if (T.size() > maxCount)
			return;

		short[] wave = byteArray2ShortArray(b, offset, count);
		int time = wave.length / 16;

		int sum = 0;
		int abssum = 0;
		short nVoice = 0;
		for (int i = 0; i < wave.length; i++) {
			sum += wave[i];
		}
		int avg = sum / wave.length;
		for (int i = 0; i < wave.length; i++)
			wave[i] -= avg;

		for (int i = 0; i < wave.length; i++) {
			short v = wave[i];
			if (v < 0) {
				v = (short) -v;
			}
			if (v > nVoice) {
				nVoice = v;
			}
			abssum += v;
		}

		int absavg = abssum / wave.length;

		if (isVadStart) {
			if (absavg > gateMuteValue) {// 检测静音
				int x = time / 20;
				if (T == null) {
					T = new ArrayList<>();
				}
				for (int i = 0; i < x; i++) {
					int fu = calFundamental(wave, i * 320, (i + 1) * 320);
					if (fu > 0 && fu < MAX_HZ)
						T.add(fu);
				}
			}
		} else {// 先VAD判断

			frameCount++;
			updateMuteVaule((short) absavg);

			if (frameCount > GET_HEAD_MUTE_FRAME && absavg > gateMuteValue) {
				isVadStart = true;
			}
		}
	}

	/**
	 * 20ms计算一次 短时自相关周期
	 * 
	 * @param wave
	 * @param offset
	 * @param count
	 */
	private int calFundamental(short[] wave, int offset, int count) {
		int N = 0;

		int n = count - offset;
		long perValue = 0;
		long[] Rx = new long[n];
		for (int k = offset; k < count; k++) {
			int index = k - offset;
			Rx[index] = 0;
			for (int i = k + 1; i < count; i++) {
				Rx[index] = Rx[index] + wave[i] * wave[i - k];

			}
		}
		for(int i = 0 ; i < n; i ++){
			if (i > 10) {
				if (Rx[i] > perValue) {
					N = i;
					perValue = Rx[i];
				}
			}
		}
//		N %= 320;
		if(N > 0)
			N = Math.round(16000 / N);

		return N;
	}

	/**
	 * 将byte[]数组转换为short[]数组
	 * @param byteData
	 * @param offset
	 * @param count
     * @return
     */
	protected short[] byteArray2ShortArray(byte[] byteData, int offset,
			int count) {
		short[] shortData = new short[count / 2];
		int shortPos = 0;
		while (offset + 1 < byteData.length && count > 1) {
			shortData[shortPos] = (short) (byteData[offset] & 0xff | (byteData[offset + 1] & 0xff) << 8);
			offset += 2;
			count -= 2;
			shortPos++;
		}
		return shortData;
	}

	/**
	 * 从开始录音中取下平均音量 计算静音值
	 * 
	 * @param value
	 *            音量
	 */
	private void updateMuteVaule(short value) {
		if (frameCount <= GET_HEAD_MUTE_FRAME) {
		} else if (frameCount > GET_HEAD_MUTE_FRAME + 10 + 1) {
		} else if (frameCount < GET_HEAD_MUTE_FRAME + 10 + 1) {
			if (value > (MAX_MUTE_VALUE + MAX_MUTE_VALUE)) {
				value = (short) (MAX_MUTE_VALUE + MAX_MUTE_VALUE);
			}
			headVoiceSum += value;
		} else {

			// 计算VAD 门限
			gateMuteValue = (int) ((headVoiceSum / GET_HEAD_MUTE_FRAME) * 1.2);
			// VAD 门限极值保护
			if (gateMuteValue > MAX_MUTE_VALUE) {
				gateMuteValue = MAX_MUTE_VALUE;
			} else {
				if (gateMuteValue < DEFAULT_MUTE_VALUE) {
					gateMuteValue = DEFAULT_MUTE_VALUE;
				}
			}
		}
	}

	/**
	 * 中值滤波
	 * 
	 * @param samples
	 * @return
	 */
	private ArrayList<Integer> getSampleByMedianFilter(
			ArrayList<Integer> samples) {
		if (samples == null || samples.size() < 3) {
			return samples;
		} else {
			try {
				// 邻域的个数
				int medianSampleCount = samples.size() / 2 + 1;
				ArrayList<Integer> newSamples = new ArrayList<Integer>();
				for (int i = 0; i < samples.size(); i++) {
					// 定义邻域
					List<Integer> medianSample = new ArrayList<Integer>();
					int count = medianSampleCount;
					int step = 1;
					// 先取左边的，再取右边的
					boolean left = true;
					medianSample.add(samples.get(i));
					while (count-- > 1) {
						int index = 0;
						if (left) {
							index = i - step;
							if (index < 0) {
								index = samples.size() - Math.abs(index);
							}
						} else {
							index = i + step;
							if (index >= samples.size()) {
								index = index - samples.size();
							}
							step++;
						}
						left = !left;
						medianSample.add(samples.get(index));
					}
					// 排序
					Collections.sort(medianSample);
					// 取中值
					if (medianSampleCount % 2 == 0) // 偶数
					{
						int avg = (medianSample.get(medianSampleCount / 2 - 1) + medianSample
								.get(medianSampleCount / 2)) / 2;
						newSamples.add(avg);
					} else // 奇数
					{
						newSamples.add(medianSample.get(medianSampleCount / 2));
					}
				}
				return newSamples;
			} catch (Exception e) {
				e.printStackTrace();

				return samples;
			}
		}
	}

}
